//====================================================================
//    COPYRIGHT(C) Innobeam
//    ALL RIGHTS RESERVED
//======================================================================
//Filename    : lan9252pram_proc.v rev 1.0
//Created On  : 2017-09-02
//Author      : shilong.zhang
//Description :This module is uesd to access LAN9252PRAM;
// through HBI bus.(require 10 iClk cycles)
//Include     : 
//Modification: 
//=====================================================================
module lan9252pram_proc (//input
					iClk   			,//typical 50Mhz(LAN9252 RD and WR need 2 iClks)
					iRst_n          ,//reset in low active.
					iWrite			,//input,enable read and writes to LAN9252 once
					iRead          	,//read enable signal
					ivNum			,//4bit input Rx/Tx dword number(must<=8)
					ivAddr			,//16bit input address(destination PRAM address)
					oDone          	,//operation done signal
					oReady         	,//next operation is ready

					ivData			,//32bit data to be written to PRAM 
					ovDpramAddrTx 	,//4bit to Tx dual port ram address(must<=ivNum)
					oRenDpram		,//1bit to Tx dual port ram Read Enable 

					ovData			,//32bit data from PRAM
					oWenDpram		,//PRAM output <ovData> and <ovDpramAddrRx> valid(high valid)
					ovDpramAddrRx	,//4bit address from PRAM(must<=ivNum)

					//lan9252_interface module interface
					ovLanData 		,//32bit data to lan9252_interface module 
					ovLanAddr		,//16bit address to lan9252_interface module 
					ivLanData 		,//32bit data from lan9252_interface module
					oLanWr         	,//write enable to lan9252_interface module
					oLanRd         	,//read enable to lan9252_interface module
					iLanDone       	//input Done signal from lan9252_interface module
					);
//========================================================================
//    parameter
//========================================================================
//data width parameter
parameter	DATA_W 				= 	16;		

//pram reg fixed address and data parameters
parameter	PRAM_RD_ADR_LEN 	= 	16'h0184;	//Low Address of PRAM_RD_ADR_LEN registor
parameter	PRAM_RD_CMD 		= 	16'h0186;	//Low Address of PRAM_RD_CMD registor
parameter	PRAM_WR_ADR_LEN 	= 	16'h0188;	//Low Address of PRAM_WR_ADR_LEN registor
parameter	PRAM_WR_CMD 		= 	16'h018A;	//Low Address of PRAM_WR_CMD registor
parameter	ADDR_RX 			= 	16'h1000;
parameter	ADDR_TX 			= 	16'h1800;

//state machine STATE parameters
parameter IDLE 					= 	4'h0000;
parameter WR_PRAM_RD_ADR_LEN 	= 	4'b0001; 
parameter WR_PRAM_RD_CMD_STA 	= 	4'b0010;
parameter RD_PRAM_RD_CMD_VAL 	= 	4'b0011;
parameter RD_PRAM_RD_DAT 		= 	4'b0100;
parameter RD_PRAM_RD_CMD_BSY 	= 	4'b0101;
parameter WR_PRAM_WR_ADR_LEN 	= 	4'b0110;
parameter WR_PRAM_WR_CMD_STA 	= 	4'b0111;
parameter RD_PRAM_WR_CMD_VAL 	= 	4'b1000;
parameter WR_PRAM_WR_DAT 		= 	4'b1001;
parameter RD_PRAM_WR_CMD_BSY 	= 	4'b1010;

//========================================================================
//    port
//========================================================================
input 							iClk,iRst_n;
input 							iWrite,iRead;

input		[3:0]				ivNum;
input 		[15:0]				ivAddr;
input 		[2*DATA_W-1:0]		ivData;
output 		[2*DATA_W-1:0]		ovData;
output 		[3:0]				ovDpramAddrTx,ovDpramAddrRx;
output 							oWenDpram;
output 							oRenDpram;
output							oDone;
output							oReady;

//lan9252_interface module interface
output		[2*DATA_W-1:0]		ovLanData; 	//32bit data to lan9252_interface module 
output		[DATA_W-1:0]		ovLanAddr;	//16bit address to lan9252_interface module 
input		[2*DATA_W-1:0]		ivLanData; 	//32bit data from lan9252_interface module
output							oLanWr;
output							oLanRd;
input							iLanDone;

//========================================================================
//    signal
//========================================================================
//lan9252_interface module interface
reg 		[2*DATA_W-1:0]		rvLanData;
reg			[DATA_W-1:0]		rvLanAddr;
reg								rLanWrEn;
reg								rLanRdEn;

//top interface
reg 							rDone;
reg								rReady;
reg								rWenDpram;
reg								rRenDpram;
reg			[9:0]				rvDpramAddrTx;
reg			[9:0]				rvDpramAddrRx;
reg			[3:0]				rvNum;
reg 		[2*DATA_W-1:0]		rvRLandata;
reg			[3:0]				rvCnt,rvCntDely;

//internal register
reg 		[3:0]				rvState_c,rvState_n;
reg			[5:0]				rvValCnt;	//number of valid words in FIFO
reg								rWrOrRdN;
reg			[2:0]				rvFIFOCnt;
reg			[15:0]				rvAddr;

reg								rWtFlag;

wire 							wIdle2wr_pram_rd_add_len_start;
wire	 						wWr_pram_rd_add_len2wr_pram_rd_cmd_sta_start;
wire 							wWr_pram_rd_cmd_sta2rd_pram_rd_cmd_val_start;
wire 							wRd_pram_rd_cmd_val2rd_pram_rd_dat_start; 
wire 							wRd_pram_rd_dat2rd_pram_rd_cmd_val_start;
wire 							wRd_pram_rd_dat2rd_pram_rd_cmd_bsy_start;
wire 							wRd_pram_rd_cmd_bsy2idle_start;
//wire 							rd_pram_rd_cmd_bsy2wr_pram_wr_add_len_start;
wire 							wIdle2wr_pram_wr_add_len_start;
wire 							wWr_pram_wr_add_len2wr_pram_wr_cmd_sta_start;
wire 							wWr_pram_wr_cmd_sta2rd_pram_wr_cmd_val_start;
wire 							wRd_pram_wr_cmd_val2wr_pram_wr_dat_start;
wire 							wWr_pram_wr_dat2rd_pram_wr_cmd_val_start;
wire 							wWr_pram_wr_dat2rd_pram_wr_cmd_bsy_start;
wire 							wRd_pram_wr_cmd_bsy2idle_start;


assign 	oWenDpram = rWenDpram;
assign	oRenDpram = rRenDpram;
assign	ovDpramAddrTx = rvCnt;
assign  ovDpramAddrRx = rvCntDely;
assign	ovData = rvRLandata;
assign	oReady = rReady;

assign	ovLanAddr = rvLanAddr;
assign	ovLanData = rvLanData;
assign	oLanWr = rLanWrEn;
assign	oLanRd = rLanRdEn;

//delay address output
always@(posedge iClk or negedge iRst_n)begin
if(!iRst_n)begin
	rvCntDely<= 4'd0;
	end
else begin
	rvCntDely<= rvCnt;
	end
end
//===========================================================================
// State machine description:
// IDLE 					wIdle2wr_pram_rd_add_len_start == 1 				-> WR_PRAM_RD_ADR_LEN
// IDLE						wIdle2wr_pram_wr_add_len_start == 1					-> WR_PRAM_WR_ADR_LEN
// WR_PRAM_RD_ADR_LEN 		wWr_pram_rd_add_len2wr_pram_rd_cmd_sta_start == 1 	-> WR_PRAM_RD_CMD_STA  
// WR_PRAM_RD_CMD_STA  		wWr_pram_rd_cmd_sta2rd_pram_rd_cmd_val_start == 1 	-> RD_PRAM_RD_CMD_VAL
// RD_PRAM_RD_CMD_VAL  		wRd_pram_rd_cmd_val2rd_pram_rd_dat_start == 1 		-> RD_PRAM_RD_DAT
// RD_PRAM_RD_DAT 	 		wRd_pram_rd_dat2rd_pram_rd_cmd_val_start == 1 		-> RD_PRAM_RD_CMD_VAL
// RD_PRAM_RD_DAT			wRd_pram_rd_dat2rd_pram_rd_cmd_bsy_start == 1		-> RD_PRAM_RD_CMD_BSY
// RD_PRAM_RD_CMD_BSY 		wRd_pram_rd_cmd_bsy2idle_start == 1					-> IDLE
// WR_PRAM_WR_ADR_LEN 		wWr_pram_wr_add_len2wr_pram_wr_cmd_sta_start == 1	-> WR_PRAM_WR_CMD_STA
// WR_PRAM_WR_CMD_STA 		wWr_pram_wr_cmd_sta2rd_pram_wr_cmd_val_start == 1	-> RD_PRAM_WR_CMD_VAL
// RD_PRAM_WR_CMD_VAL 		wRd_pram_wr_cmd_val2wr_pram_wr_dat_start == 1		-> WR_PRAM_WR_DAT
// WR_PRAM_WR_DAT 			wWr_pram_wr_dat2rd_pram_wr_cmd_val_start == 1     	-> RD_PRAM_WR_CMD_VAL
// WR_PRAM_WR_DAT 			wWr_pram_wr_dat2rd_pram_wr_cmd_bsy_start == 1		-> RD_PRAM_WR_CMD_BSY
// RD_PRAM_WR_CMD_BSY 		wRd_pram_wr_cmd_bsy2idle_start == 1					-> IDLE
//===========================================================================
//第一段：同步时序always模块，格式化描述次态寄存器迁移到现态寄存器(不需更改）
always@(posedge iClk or negedge iRst_n)begin
if(!iRst_n)begin
	rvState_c <= IDLE;
	end
else begin
	rvState_c <= rvState_n;
	end
end

//第二段：组合逻辑always模块，描述状态转移条件判断
always@(*)begin
case(rvState_c)
IDLE:begin
	if(wIdle2wr_pram_rd_add_len_start)begin
		rvState_n = WR_PRAM_RD_ADR_LEN;
		end
	else if(wIdle2wr_pram_wr_add_len_start) begin
		rvState_n = WR_PRAM_WR_ADR_LEN;
		end
	else begin
		rvState_n = rvState_c;
		end
	end
WR_PRAM_RD_ADR_LEN:begin
	if(wWr_pram_rd_add_len2wr_pram_rd_cmd_sta_start)begin
		rvState_n = WR_PRAM_RD_CMD_STA;
		end
	else begin
		rvState_n = rvState_c;
		end
	end
WR_PRAM_RD_CMD_STA:begin
	if(wWr_pram_rd_cmd_sta2rd_pram_rd_cmd_val_start)begin
		rvState_n = RD_PRAM_RD_CMD_VAL;
		end
	else begin
		rvState_n = rvState_c;
		end
	end
RD_PRAM_RD_CMD_VAL:begin
	if(wRd_pram_rd_cmd_val2rd_pram_rd_dat_start)begin
		rvState_n = RD_PRAM_RD_DAT;
		end
	else begin
		rvState_n = rvState_c;
		end
	end
RD_PRAM_RD_DAT:begin
	if(wRd_pram_rd_dat2rd_pram_rd_cmd_val_start)begin
		rvState_n = RD_PRAM_RD_CMD_VAL;
		end
	else if(wRd_pram_rd_dat2rd_pram_rd_cmd_bsy_start)begin
		rvState_n = RD_PRAM_RD_CMD_BSY;
		end
	else begin
		rvState_n = rvState_c;
		end
	end
RD_PRAM_RD_CMD_BSY:begin
	if(wRd_pram_rd_cmd_bsy2idle_start)begin
		rvState_n = IDLE;
		end
	else begin
		rvState_n = rvState_c;
		end
	end
WR_PRAM_WR_ADR_LEN:begin
	if(wWr_pram_wr_add_len2wr_pram_wr_cmd_sta_start)begin
		rvState_n = WR_PRAM_WR_CMD_STA;
		end
	else begin
		rvState_n = rvState_c;
		end
	end
WR_PRAM_WR_CMD_STA:begin
	if(wWr_pram_wr_cmd_sta2rd_pram_wr_cmd_val_start)begin
		rvState_n = RD_PRAM_WR_CMD_VAL;
		end
	else begin
		rvState_n = rvState_c;
		end
	end
RD_PRAM_WR_CMD_VAL:begin
	if(wRd_pram_wr_cmd_val2wr_pram_wr_dat_start)begin
		rvState_n = WR_PRAM_WR_DAT;
		end
	else begin
		rvState_n = rvState_c;
		end
	end
WR_PRAM_WR_DAT:begin
	if(wWr_pram_wr_dat2rd_pram_wr_cmd_val_start)begin
		rvState_n = RD_PRAM_WR_CMD_VAL;
		end
	else if(wWr_pram_wr_dat2rd_pram_wr_cmd_bsy_start) begin
		rvState_n = RD_PRAM_WR_CMD_BSY;
		end
	else begin
		rvState_n = rvState_c;
		end
	end
RD_PRAM_WR_CMD_BSY:begin
	if(wRd_pram_wr_cmd_bsy2idle_start)begin
		rvState_n = IDLE;
		end
	else begin
		rvState_n = rvState_c;
		end
	end
default:begin
	rvState_n = IDLE;
	end
endcase
end
//第三段：设计转移条件
assign wIdle2wr_pram_rd_add_len_start = (rvState_c == IDLE) && (iRead == 1'b1) ;
assign wWr_pram_rd_add_len2wr_pram_rd_cmd_sta_start = (rvState_c == WR_PRAM_RD_ADR_LEN) && (iLanDone == 1'b1);
assign wWr_pram_rd_cmd_sta2rd_pram_rd_cmd_val_start = (rvState_c == WR_PRAM_RD_CMD_STA) && (iLanDone == 1'b1);
assign wRd_pram_rd_cmd_val2rd_pram_rd_dat_start = (rvState_c == RD_PRAM_RD_CMD_VAL) && (iLanDone == 1'b1) && 
(ivLanData[0] == 1'b1); //pram read FIFO valid
assign wRd_pram_rd_dat2rd_pram_rd_cmd_val_start = (rvState_c == RD_PRAM_RD_DAT) && (iLanDone == 1'b1) && 
(rvValCnt == 6'd0) && (rvCnt != rvNum-1'b1); //FIFO is empty,need to read PRAM_RD_CMD registor VALID again
assign wRd_pram_rd_dat2rd_pram_rd_cmd_bsy_start = (rvState_c == RD_PRAM_RD_DAT) && (iLanDone == 1'b1) && 
(rvCnt == rvNum-1'b1);//All datas have been received,need to check BUSY
assign wRd_pram_rd_cmd_bsy2idle_start = (rvState_c == RD_PRAM_RD_CMD_BSY) && (iLanDone == 1'b1) && 
(ivLanData[31] == 1'b0);//Not BUSY,pram read is done.
assign wIdle2wr_pram_wr_add_len_start = (rvState_c == IDLE) && (iWrite == 1'b1) ;
assign wWr_pram_wr_add_len2wr_pram_wr_cmd_sta_start = (rvState_c == WR_PRAM_WR_ADR_LEN) && (iLanDone == 1'b1);
assign wWr_pram_wr_cmd_sta2rd_pram_wr_cmd_val_start = (rvState_c == WR_PRAM_WR_CMD_STA) && (iLanDone == 1'b1);
assign wRd_pram_wr_cmd_val2wr_pram_wr_dat_start = (rvState_c == RD_PRAM_WR_CMD_VAL) && (iLanDone == 1'b1) && 
(ivLanData[0] == 1'b1);//pram write FIFO valid
assign wWr_pram_wr_dat2rd_pram_wr_cmd_val_start = (rvState_c == WR_PRAM_WR_DAT) && (iLanDone == 1'b1) && 
(rvValCnt == 6'd0) && (rvCnt != rvNum);//FIFO is empty,need to read PRAM_WR_CMD registor VALID again
assign wWr_pram_wr_dat2rd_pram_wr_cmd_bsy_start = (rvState_c == WR_PRAM_WR_DAT) && (iLanDone == 1'b1) && 
(rvCnt == rvNum);//FIFO is empty,need to read PRAM_WR_CMD registor VALID again
assign wRd_pram_wr_cmd_bsy2idle_start = (rvState_c == RD_PRAM_WR_CMD_BSY) && (iLanDone == 1'b1) && 
(ivLanData[31] == 1'b0) ;//Not BUSY,pram write is done.

//第四段：同步时序always模块，格式化描述寄存器输出（可有多个输出）
always  @(posedge iClk or negedge iRst_n)begin
if(!iRst_n)begin
	rvLanAddr <= 16'd0;
	rvLanData <= 32'd0;
	rvRLandata <= 32'd0;
	rLanWrEn <= 1'b0;
	rWtFlag <= 1'b0;
	rvValCnt <= 6'd0;
	rvNum <= 4'd0;
	rvCnt <= 4'd0;
	rvFIFOCnt <= 3'd0;
	rvAddr <= 16'h0000;
	rWenDpram <= 1'b0;
	rRenDpram <= 1'b0;
	rReady <= 1'b1;
	end
else begin
case(rvState_c)
IDLE:begin
	rLanWrEn <= 0;
	// rDone <= 0;
	rWtFlag <= 1'b0;
	rvFIFOCnt <= 3'd0;
	if(iWrite == 1'b1) begin
		rvNum <= ivNum;
		rvCnt <= 4'd0;
		rvAddr <= ivAddr;
		rReady <= 1'b0;
		end
	else if(iRead) begin
		rvNum <= ivNum;
		rvCnt <= 4'd0;
		rvAddr <= ivAddr;
		rReady <= 1'b0;
		end
	end
WR_PRAM_RD_ADR_LEN:begin
	if(rWtFlag==1'b0) begin
		rLanWrEn <= 1'b1;
		rvLanAddr <= PRAM_RD_ADR_LEN;//;
		rvLanData <= {10'd0,rvNum,2'b00,rvAddr}; //change dword number(NUM_RX) to number of bytes
		rWtFlag <= 1'b1;
		end
	else begin
		rLanWrEn <= 1'b0;
		end
	if(iLanDone == 1'b1) begin
		rWtFlag <= 1'b0;
		end
	end
WR_PRAM_RD_CMD_STA:begin
	if(rWtFlag==1'b0) begin
		rLanWrEn <= 1'b1;
		rvLanAddr <= PRAM_RD_CMD;//;
		rvLanData <= 32'h8000_0000; //start read
		rWtFlag <= 1'b1;
		end
	else begin
		rLanWrEn <= 1'b0;
		end
	if(iLanDone == 1'b1) begin
		rWtFlag <= 1'b0;
		end
	end
RD_PRAM_RD_CMD_VAL:begin
	rWenDpram <= 1'b0; //write dpram
	if(rWtFlag==1'b0) begin
		rLanRdEn <= 1'b1;
		rvLanAddr <= PRAM_RD_CMD;//;
		rvLanData <= rvLanData;
		rWtFlag <= 1'b1;
		end
	else begin
		rLanRdEn <= 1'b0;
		end
	if(iLanDone == 1'b1) begin
		rWtFlag <= 1'b0;
		rvValCnt <= ivLanData[12:8];	//number of valid data(dword:32bit)
		rvFIFOCnt <= 3'h0;
		end
	end
RD_PRAM_RD_DAT:begin
	if(rWtFlag==1'b0) begin
		rLanRdEn <= 1'b1;
		rvLanAddr <= {12'h000,rvFIFOCnt,1'b0};//;
		rvLanData <= rvLanData;
		rWtFlag <= 1'b1;
		rvValCnt <= rvValCnt - 1'b1;
		if(rvFIFOCnt == 3'h7)begin//>14,set to 0;
			rvFIFOCnt <= 3'h0;
			end
		else begin
			rvFIFOCnt <= rvFIFOCnt + 1'b1;
			end
		end
	else begin
		rLanRdEn <= 1'b0;
		end
	if(iLanDone == 1'b1) begin
		rWtFlag <= 1'b0;
		rvRLandata <= ivLanData;
		rWenDpram <= 1'b1; //write dpram
		if(rvCnt < rvNum)begin
			rvCnt <= rvCnt + 1'b1;
			end
		else begin
			rvCnt <= rvCnt;
			end
		end
	else begin
		rWenDpram <= 1'b0; //write dpram
		end
	end
RD_PRAM_RD_CMD_BSY:begin
	rWenDpram <= 1'b0;
	if(rWtFlag==1'b0) begin
		rLanRdEn <= 1'b1;
		rvLanAddr <= PRAM_RD_CMD;//;
		rvLanData <= rvLanData;
		rWtFlag <= 1'b1;
		end
	else begin
		rLanRdEn <= 1'b0;
		end
	if(iLanDone == 1'b1) begin
		rWtFlag <= 1'b0;
		if(ivLanData[31] == 1'b0)begin
			rReady <= 1'b1;
			end
		end
	end

WR_PRAM_WR_ADR_LEN:begin
	if(rWtFlag==1'b0) begin
		rLanWrEn <= 1'b1;
		rvLanAddr <= PRAM_WR_ADR_LEN;//;
		rvLanData <= {10'd0,rvNum,2'b00,rvAddr}; //change dword number(NUM_RX) to number of bytes
		rWtFlag <= 1'b1;
		end
	else begin
		rLanWrEn <= 1'b0;
		end
	if(iLanDone == 1'b1) begin
		rWtFlag <= 1'b0;
		end
	end
WR_PRAM_WR_CMD_STA:begin
	if(rWtFlag==1'b0) begin
		rLanWrEn <= 1'b1;
		rvLanAddr <= PRAM_WR_CMD;//;
		rvLanData <= 32'h8000_0000; //start read
		rWtFlag <= 1'b1;
		rRenDpram <= 1'b1; //read dpram
		end
	else begin
		rLanWrEn <= 1'b0;
		rRenDpram <= 1'b0; //read dpram
		end
	if(iLanDone == 1'b1) begin
		rWtFlag <= 1'b0;
		end
	end
RD_PRAM_WR_CMD_VAL:begin
	if(rWtFlag==1'b0) begin
		rLanRdEn <= 1'b1;
		rvLanAddr <= PRAM_WR_CMD;//;
		rvLanData <= rvLanData;
		rWtFlag <= 1'b1;
		end
	else begin
		rLanRdEn <= 1'b0;
		end
	if(iLanDone == 1'b1) begin
		rWtFlag <= 1'b0;
		rvValCnt <= ivLanData[12:8];	//number of valid data(dword:32bit)
		rvFIFOCnt <= 3'h0;
		end
	end
WR_PRAM_WR_DAT:begin
	if(rWtFlag==1'b0) begin
		rRenDpram <= 1'b1; //read dpram(read times == ivNum+1,but the last read data will be ignored)
		rLanWrEn <= 1'b1;
		rvLanAddr <= {12'h001,rvFIFOCnt,1'b0};//
		rvLanData <= ivData;//last time read out data(rRenDpram high)
		rWtFlag <= 1'b1;
		rvValCnt <= rvValCnt - 1'b1;
		if(rvCnt < rvNum)begin
			rvCnt <= rvCnt + 1'b1;
			end
		if(rvFIFOCnt == 3'h7) begin
			rvFIFOCnt <= 3'h0;
			end
		else begin
			rvFIFOCnt <= rvFIFOCnt + 1'b1;
			end
		end
	else begin
		rLanWrEn <= 1'b0;
		rRenDpram <= 1'b0; //read dpram
		end
	if(iLanDone == 1'b1) begin
		rWtFlag <= 1'b0;
		if(rvCnt == rvNum)begin
			end
		end
	end
RD_PRAM_WR_CMD_BSY:begin
	if(rWtFlag==1'b0) begin
		rLanRdEn <= 1'b1;
		rvLanAddr <= PRAM_WR_CMD;//;
		rvLanData <= rvLanData;
		rWtFlag <= 1'b1;
		end
	else begin
		rLanRdEn <= 1'b0;
		end
	if(iLanDone == 1'b1) begin
		rWtFlag <= 1'b0;
		if(ivLanData[31] == 1'b0)begin
			rReady <= 1'b1;
			end
		end
	end
default:begin
	rLanWrEn <= 1'b0;
	rLanRdEn <= 1'b0;
	end
endcase
end
end

always  @(posedge iClk or negedge iRst_n)begin
if(!iRst_n)begin
	rDone <= 1'b0;
	end
else begin
	if(wRd_pram_rd_dat2rd_pram_rd_cmd_bsy_start || wWr_pram_wr_dat2rd_pram_wr_cmd_bsy_start)begin
		rDone <= 1'b1;
		end
	else begin
		rDone <= 1'b0;
		end	
	end
end

assign	oDone = rDone;//wRd_pram_rd_dat2rd_pram_rd_cmd_bsy_start || wWr_pram_wr_dat2rd_pram_wr_cmd_bsy_start;

endmodule
					
					
					
					
					